// -*- mode:c++ -*-

// Copyright (c) 2009 The University of Edinburgh
// Copyright (c) 2021 IBM Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

////////////////////////////////////////////////////////////////////
//
// Memory-format instructions
//

def template LoadStoreDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        /// Constructor.
        %(class_name)s(ExtMachInst machInst);

        Fault execute(ExecContext *, trace::InstRecord *) const override;
        Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
        Fault completeAcc(PacketPtr, ExecContext *,
                          trace::InstRecord *) const override;
    };
}};


def template LoadStoreConstructor {{
    %(class_name)s::%(class_name)s(ExtMachInst machInst)
         : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
    }
}};


def template LoadExecute {{
    Fault %(class_name)s::execute(ExecContext *xc,
                                  trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;
        Msr msr = xc->tcBase()->getReg(int_reg::Msr);

        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (fault == NoFault) {
            fault = msr.le ?
                    readMemAtomicLE(xc, traceData, EA, Mem, memAccessFlags) :
                    readMemAtomicBE(xc, traceData, EA, Mem, memAccessFlags);
            %(memacc_code)s;
        }

        if (fault == NoFault) {
            %(update_code)s;
            %(op_wb)s;
        }

        return fault;
    }
}};


def template LoadInitiateAcc {{
    Fault %(class_name)s::initiateAcc(ExecContext *xc,
                                      trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_src_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (fault == NoFault) {
            fault = initiateMemRead(xc, traceData, EA, Mem, memAccessFlags);
        }

        return fault;
    }
}};


def template LoadCompleteAcc {{
    Fault %(class_name)s::completeAcc(PacketPtr pkt,
                                      ExecContext *xc,
                                      trace::InstRecord *traceData) const
    {
        [[maybe_unused]] Addr EA;
        Fault fault = NoFault;
        Msr msr = xc->tcBase()->getReg(int_reg::Msr);

        %(op_decl)s;
        EA = pkt->req->getVaddr();
        %(op_rd)s;

        if (msr.le)
            getMemLE(pkt, Mem, traceData);
        else
            getMemBE(pkt, Mem, traceData);

        if (fault == NoFault) {
            %(memacc_code)s;
        }

        if (fault == NoFault) {
            %(update_code)s;
            %(op_wb)s;
        }

        return fault;
    }
}};


def template StoreExecute {{
    Fault %(class_name)s::execute(ExecContext *xc,
                                  trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;
        Msr msr = xc->tcBase()->getReg(int_reg::Msr);

        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (fault == NoFault) {
            %(memacc_code)s;
        }

        if (fault == NoFault) {
            fault = msr.le ?
                    writeMemAtomicLE(xc, traceData, Mem, EA, memAccessFlags,
                                     NULL) :
                    writeMemAtomicBE(xc, traceData, Mem, EA, memAccessFlags,
                                     NULL);
        }

        if (fault == NoFault) {
            %(update_code)s;
            %(op_wb)s;
        }

        return fault;
    }
}};


def template StoreInitiateAcc {{
    Fault %(class_name)s::initiateAcc(ExecContext *xc,
                                      trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;
        Msr msr = xc->tcBase()->getReg(int_reg::Msr);

        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (fault == NoFault) {
            %(memacc_code)s;
        }

        if (fault == NoFault) {
            fault = msr.le ?
                    writeMemTimingLE(xc, traceData, Mem, EA, memAccessFlags,
                                     NULL) :
                    writeMemTimingBE(xc, traceData, Mem, EA, memAccessFlags,
                                     NULL);
        }

        return fault;
    }
}};


def template StoreCompleteAcc {{
    Fault %(class_name)s::completeAcc(PacketPtr pkt, ExecContext *xc,
                                      trace::InstRecord *traceData) const
    {
        [[maybe_unused]] Addr EA;
        Fault fault = NoFault;

        %(op_decl)s;
        EA = pkt->req->getVaddr();
        %(op_rd)s;

        // Need to write back any potential address register update
        if (fault == NoFault) {
            %(update_code)s;
            %(op_wb)s;
        }

        return fault;
    }
}};


// The generic memory operation generator. This is called when two versions
// of an instruction are needed - when Ra == 0 and otherwise. This is so
// that instructions can use the value 0 when Ra == 0 but avoid having a
// dependence on Ra.
let {{

def GenMemOp(name, Name, memacc_code, update_code, ea_code, ea_code_ra0, base,
             load_or_store, mem_flags = [], inst_flags = []):

    # First the version where Ra is non-zero
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code,
                      memacc_code, update_code,
                      mem_flags, inst_flags,
                      base_class = base,
                      decode_template = CheckRaDecode,
                      exec_template_base = load_or_store)

    # Now another version where Ra == 0
    (header_output_ra0, decoder_output_ra0, _, exec_output_ra0) = \
        LoadStoreBase(name, Name + 'RaZero', ea_code_ra0,
                      memacc_code, update_code,
                      mem_flags, inst_flags,
                      base_class = base,
                      exec_template_base = load_or_store)

    # Finally, add to the other outputs
    header_output += header_output_ra0
    decoder_output += decoder_output_ra0
    exec_output += exec_output_ra0
    return (header_output, decoder_output, decode_block, exec_output)

}};


def format LoadIndexOp(memacc_code, update_code = {{ }},
                       ea_code = {{ EA = Ra + Rb; }},
                       ea_code_ra0 = {{ EA = Rb; }},
                       mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        GenMemOp(name, Name, memacc_code, update_code, ea_code, ea_code_ra0,
                 'MemIndexOp', 'Load', mem_flags, inst_flags)
}};


def format StoreIndexOp(memacc_code, update_code = {{ }},
                        ea_code = {{ EA = Ra + Rb; }},
                        ea_code_ra0 = {{ EA = Rb; }},
                        mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        GenMemOp(name, Name, memacc_code, update_code, ea_code, ea_code_ra0,
                 'MemIndexOp', 'Store', mem_flags, inst_flags)
}};


def format LoadIndexUpdateOp(memacc_code, ea_code = {{ EA = Ra + Rb; }},
                             mem_flags = [], inst_flags = []) {{

    # Add in the update code
    update_code = 'Ra = EA;'

    # Generate the class
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, update_code,
                      mem_flags, inst_flags,
                      base_class = 'MemIndexOp',
                      decode_template = CheckRaRtDecode,
                      exec_template_base = 'Load')
}};


def format StoreIndexUpdateOp(memacc_code, ea_code = {{ EA = Ra + Rb; }},
                              mem_flags = [], inst_flags = []) {{

    # Add in the update code
    update_code = 'Ra = EA;'

    # Generate the class
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, update_code,
                      mem_flags, inst_flags,
                      base_class = 'MemIndexOp',
                      decode_template = CheckRaZeroDecode,
                      exec_template_base = 'Store')
}};


def format LoadDispOp(memacc_code, ea_code = {{ EA = Ra + d; }},
                      ea_code_ra0 = {{ EA = d; }},
                      mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        GenMemOp(name, Name, memacc_code, '', ea_code, ea_code_ra0,
                 'MemDispOp', 'Load', mem_flags, inst_flags)
}};


def format StoreDispOp(memacc_code, ea_code = {{ EA = Ra + d; }},
                       ea_code_ra0 = {{ EA = d; }},
                       mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        GenMemOp(name, Name, memacc_code, '', ea_code, ea_code_ra0,
                 'MemDispOp', 'Store', mem_flags, inst_flags)
}};


def format LoadDispShiftOp(memacc_code,
                           ea_code = {{ EA = Ra + (ds << 2); }},
                           ea_code_ra0 = {{ EA = (ds << 2); }},
                           mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        GenMemOp(name, Name, memacc_code, '', ea_code, ea_code_ra0,
                 'MemDispShiftOp', 'Load', mem_flags, inst_flags)
}};


def format StoreDispShiftOp(memacc_code,
                            ea_code = {{ EA = Ra + (ds << 2); }},
                            ea_code_ra0 = {{ EA = (ds << 2); }},
                            mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        GenMemOp(name, Name, memacc_code, '', ea_code, ea_code_ra0,
                 'MemDispShiftOp', 'Store', mem_flags, inst_flags)
}};


def format LoadDispUpdateOp(memacc_code, ea_code = {{ EA = Ra + d; }},
                            mem_flags = [], inst_flags = []) {{

    # Add in the update code
    update_code = 'Ra = EA;'

    # Generate the class
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, update_code,
                      mem_flags, inst_flags,
                      base_class = 'MemDispOp',
                      decode_template = CheckRaRtDecode,
                      exec_template_base = 'Load')
}};


def format StoreDispUpdateOp(memacc_code, ea_code = {{ EA = Ra + d; }},
                             mem_flags = [], inst_flags = []) {{

    # Add in the update code
    update_code = 'Ra = EA;'

    # Generate the class
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, update_code,
                      mem_flags, inst_flags,
                      base_class = 'MemDispOp',
                      decode_template = CheckRaZeroDecode,
                      exec_template_base = 'Store')
}};


def format LoadDispShiftUpdateOp(memacc_code,
                                 ea_code = {{ EA = Ra + (ds << 2); }},
                                 mem_flags = [], inst_flags = []) {{

    # Add in the update code
    update_code = 'Ra = EA;'

    # Generate the class
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, update_code,
                      mem_flags, inst_flags,
                      base_class = 'MemDispShiftOp',
                      decode_template = CheckRaRtDecode,
                      exec_template_base = 'Load')
}};


def format StoreDispShiftUpdateOp(memacc_code,
                                  ea_code = {{ EA = Ra + (ds << 2); }},
                                  mem_flags = [], inst_flags = []) {{

    # Add in the update code
    update_code = 'Ra = EA;'

    # Generate the class
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, update_code,
                      mem_flags, inst_flags,
                      base_class = 'MemDispShiftOp',
                      decode_template = CheckRaZeroDecode,
                      exec_template_base = 'Store')
}};
